package com.igoal.dao;

import java.util.Date;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;

import com.igoal.entity.DataStatus;
import com.igoal.entity.GoalStatusEnum;
import com.igoal.entity.UserGoal;
import com.igoal.entity.UserGoalPk;
import com.igoal.utils.CalendarUtils;
import com.igoal.web.Constants;

@Repository("userGoalDAO")
public class UserGoalDAOImpl extends GenericDaoHibernate<UserGoal, UserGoalPk> implements
		UserGoalDAO {

	public UserGoalDAOImpl() {
		super(UserGoal.class);
	}

	public List<UserGoal> findByUserId(Long userId) {
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.user.id", userId));
		dc.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		getByStatus(dc, DataStatus.USING);
		
		List<UserGoal> result = findByCriteria(dc);
		
		return result;
	}
	
	public UserGoal findByPK(Long userId, Long goalId){
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.user.id", userId));
		dc.add(Restrictions.eq("pk.goal.id", goalId));
		getByStatus(dc, DataStatus.USING);
		
		List<UserGoal> result = findByCriteria(dc);
		if(result.size() > 0)
			return result.get(0);
		
		return null;
	}

	public boolean exists(Long userId, Long goalId) {
		UserGoal userGoal = findByPK(userId, goalId);
		if(userGoal != null) return true;
		
		return false;
	}

	public List<UserGoal> getUserGoal(Long userId, boolean completed) {
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.user.id", userId));
		GoalStatusEnum goalStatus = completed ? GoalStatusEnum.FINISHED : GoalStatusEnum.DOING;
		dc.add(Restrictions.eq("goalStatus", goalStatus));
		getByStatus(dc, DataStatus.USING);
		dc.addOrder(Order.desc("updateDate"));
		
		List<UserGoal> result = findByCriteria(dc);		
		return result;
	}
	
	public int countDoUsers(Long goalId) {
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.goal.id", goalId));
		getByStatus(dc, DataStatus.USING);
		return countByCriteria(dc);
	}
	
	public int countDoingUsers(Long goalId) {
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.goal.id", goalId));
		dc.add(Restrictions.eq("goalStatus", GoalStatusEnum.DOING));
		getByStatus(dc, DataStatus.USING);
		return countByCriteria(dc);
	}
	
	public int countDoneUsers(Long goalId) {
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.goal.id", goalId));
		dc.add(Restrictions.eq("goalStatus", GoalStatusEnum.FINISHED));
		getByStatus(dc, DataStatus.USING);
		return countByCriteria(dc);
	}
	
	public List<UserGoal> getRandomUserDoGoal(Long goalId) {
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.goal.id", goalId));
		getByStatus(dc, DataStatus.USING);
		getRandomEntities(dc);
		return findByCriteria(dc, 0, Constants.RANDOM_USER_DO_GOAL_NUMBER);
	}

	public List<UserGoal> getUsersDoGoal(Long goalId, int pageNumber) {
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.goal.id", goalId));
		getByStatus(dc, DataStatus.USING);
		dc.addOrder(Order.desc("updateDate"));
		List<UserGoal> result = findByCriteria(dc,
				Constants.USER_DO_GOAL_PAGE_SIZE * (pageNumber - 1),
				Constants.USER_DO_GOAL_PAGE_SIZE);
		return result;
	}

	public List<UserGoal> getUserGoal(Long goalId, boolean completed,
			boolean any, int numberOfUser) {
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.goal.id", goalId));
		if(!any){
			GoalStatusEnum goalStatus = completed ? GoalStatusEnum.FINISHED : GoalStatusEnum.DOING;
			dc.add(Restrictions.eq("goalStatus", goalStatus));
		}
		dc.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		getByStatus(dc, DataStatus.USING);
		
		List<UserGoal> result = findByCriteria(dc, 0, numberOfUser);
		return result;
	}
	
	public List<UserGoal> getCreatedUserGoalInToday(Long userId){
		Date today = new Date();
		return getCreatedUserGoalInDate(userId, today);
	}
	
	public List<UserGoal> getCreatedUserGoalInDate(Long userId, Date date){
		if(date == null)
			return null;
		
		Date beginDate = CalendarUtils.getStartDate(date);
		Date endDate = CalendarUtils.getEndDate(date);
		
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.user.id", userId));
		dc.add(Restrictions.between("createDate", beginDate, endDate));
		getByStatus(dc, DataStatus.USING);
		
		List<UserGoal> result = findByCriteria(dc);
		
		return result;
	}
	
	public int countCreatedUserGoalInToday(Long userId){
		Date today = new Date();
		return countCreatedUserGoalInDate(userId, today);
	}
	
	public int countCreatedUserGoalInDate(Long userId, Date date){
		if(date == null)
			return 0;
		
		Date beginDate = CalendarUtils.getStartDate(date);
		Date endDate = CalendarUtils.getEndDate(date);
		
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.user.id", userId));
		dc.add(Restrictions.between("createDate", beginDate, endDate));
		getByStatus(dc, DataStatus.USING);//TODO: re-check; count using or all?
		
		int result = countByCriteria(dc);
		
		return result;
	}
	
	public int countGoals(Long userId, boolean finished){
		DetachedCriteria dc = DetachedCriteria.forClass(UserGoal.class);
		dc.add(Restrictions.eq("pk.user.id", userId));
		if(finished){
			dc.add(Restrictions.eq("goalStatus", GoalStatusEnum.FINISHED));
		}else{
			dc.add(Restrictions.ne("goalStatus", GoalStatusEnum.FINISHED));
		}
		getByStatus(dc, DataStatus.USING);
		
		return countByCriteria(dc);
	}
}
